#include <dde_kit/interrupt.h>
#include <dde_kit/panic.h>

#include "conf.h"

#include <eku/proc.h>
#include <eku/sync.h>
#include <eku/stdio.h>
#include <eku/stdlib.h>
#include <eku/system.h>

#include <inc/x86_lock.h>
#include <inc/error.h>

/**
 * Attach to hardware interrupt
 *
 * \param irq          IRQ number to attach to
 * \param shared       set to 1 if interrupt sharing is supported; set to 0
 *                     otherwise
 * \param thread_init  called just after DDE kit internal init and before any
 *                     other function
 * \param handler      IRQ handler for interrupt irq
 * \param priv         private token (argument for thread_init and handler)
 *
 * \return             attachment state
 * \retval 0           success
 * \retval !0          error
 */

spin_lock_t dk_irq_attach_lock;
struct dk_irq_hook_list *dk_irq_hooks_attaching[32];
struct dk_irq_hook_list *dk_irq_hooks[32];

static int dk_intr_attach_pid = -1;
static int
dk_intr_attach_process(void *_list)
{
	 dde_kit_thread_adopt_myself("DKINTR");
	 
	 printf("dde intr process %d\n", cur_pid());
	 int flags[32];
	 int i;
	 
	 while (true)
	 {
		  int to_wait = 1;
		  for (i = 0; i != 32; ++ i)
		  {
			   flags[i] = 0;
			   if (dk_irq_hooks[i] != NULL)
			   {
					uint64_t count;
					int ret;
					ret = ips_recv_try(i, 0, &count);
					
					if (ret == -E_INVAL)
					{
						 ips_recv(sysconf.irq_ap[i], i);
						 ret = ips_recv_try(i, 0, &count);
					}
					
					if (ret == 0)
					{
						 to_wait = 0;
						 flags[i] = 1;
					}
			   }
		  }

		  for (i = 0; i != 32; ++ i)
		  {
			   if (flags[i])
			   {
					struct dk_irq_hook_list *cur = dk_irq_hooks[i];
					while (1)
					{
						 notify(cur->pid);
						 if ((cur = cur->next) == dk_irq_hooks[i]) break;
					}
			   }
		  }

		  spl_acquire(&dk_irq_attach_lock);

		  for (i = 0; i != 32; ++ i)
		  {
			   if (dk_irq_hooks_attaching[i] != NULL)
			   {
					if (dk_irq_hooks[i] == NULL)
						 dk_irq_hooks[i] = dk_irq_hooks_attaching[i];
					else
					{
						 struct dk_irq_hook_list *t = dk_irq_hooks[i]->prev,
							  *ta = dk_irq_hooks_attaching[i]->prev;
						 
						 t->next = dk_irq_hooks_attaching[i];
						 ta->next = dk_irq_hooks[i];
						 
						 dk_irq_hooks_attaching[i]->prev = t;
						 dk_irq_hooks[i]->prev = ta;
					}
					to_wait = 0;
					dk_irq_hooks_attaching[i] = NULL;
			   }
		  }

		  spl_release(&dk_irq_attach_lock);
		  if (to_wait)
		  {
			   wait_try();
		  }
	 }
	 	 
	 return 0;
}

static void
dk_intr_attach_thread_container(void *_)
{
	 struct dk_irq_hook_list *list = (struct dk_irq_hook_list *)_;
	 list->thread_init(list->priv);

	 while (1)
	 {
		  wait_try();
		  list->handler(list->priv);
	 }
}

int
dde_kit_interrupt_attach(int irq, int shared, void(*thread_init)(void *),
						  void(*handler)(void *), void *priv)
{
	 struct dk_irq_hook_list *list = (struct dk_irq_hook_list *)
		  malloc(sizeof(struct dk_irq_hook_list));

	 list->attach = 1;
	 list->irq = irq;
	 list->thread_init = thread_init;
	 list->handler = handler;
	 list->priv = priv;
	 list->pid = process_create_wait((void(*)(void *))dk_intr_attach_thread_container, list, 8192);
	 notify_ex(list->pid, WLEVEL_START);
	 
	 spl_acquire(&dk_irq_attach_lock);

	 /* Here we assume all attachings are shared */
	 if (dk_irq_hooks_attaching[irq] == NULL)
	 {
		  dk_irq_hooks_attaching[irq] = list;
		  list->next = list->prev = list;
	 }
	 else
	 {
		  list->next = dk_irq_hooks_attaching[irq];
		  list->prev = list->next->prev;
		  list->next->prev = list;
		  list->prev->next = list;
	 }

	 if (dk_intr_attach_pid == -1)
	 {
		  dk_intr_attach_pid =
			   process_create_wait((void(*)(void *))dk_intr_attach_process, NULL, 8192);
		  notify_ex(dk_intr_attach_pid, WLEVEL_START);
	 }
	 
	 spl_release(&dk_irq_attach_lock);
	 /* TODO ERROR DETECT */
	 return 0;
}

/**
 * Detach from a previously attached interrupt.
 *
 * \param irq          IRQ number
 */
void
dde_kit_interrupt_detach(int irq)
{
	 printf("NOT IMPLEMENTED\n");
}

/**
 * Block interrupt.
 *
 * \param irq          IRQ number to block
 */
void
dde_kit_interrupt_disable(int irq)
{
	 // pic_disable(irq);
}

/**
 * Enable interrupt.
 *
 * \param irq          IRQ number to block
 */
void
dde_kit_interrupt_enable(int irq)
{
	 // pic_enable(irq);
}

#include <eku/symbol.h>

void
dde_kit_intr_symbol(void)
{
	 export_symbol("dde_kit_interrupt_attach", &dde_kit_interrupt_attach, 1);
	 export_symbol("dde_kit_interrupt_detach", &dde_kit_interrupt_detach, 1);
	 export_symbol("dde_kit_interrupt_disable", &dde_kit_interrupt_disable, 1);
	 export_symbol("dde_kit_interrupt_enable", &dde_kit_interrupt_enable, 1);
}
